#ifndef OCTREE_H_INCLUDED
#define OCTREE_H_INCLUDED

#include <list>
#include "map/worldmesh.h"
#include "scene/aabb.h"
#include "resources/materialmanager.h"

using std::list;

namespace engine {
		
	namespace scene {
		using engine::map::WorldMesh;
		using engine::map::WorldTriangle;
		using engine::map::WorldVertex;
		
		using engine::scene::AABB;
		
		using resources::MaterialResource;
		
		class MiniNode {
			private:
				typedef list< shared_ptr<MiniNode> >::const_iterator childIter;
				list< shared_ptr<MiniNode> > m_Children;
			protected:
				void applyChildren() {
					for (childIter it = m_Children.begin(); it != m_Children.end(); ++it) {
						(*it)->apply();
					}
				}
			public:
				virtual ~MiniNode() {
					m_Children.clear();
				}
					
					
				void attachNode(shared_ptr<MiniNode> child);
				virtual void apply() = 0;
		};
		
		
		///Generic geometry node
		class MiniGeomNode : public MiniNode {
			private:
				//Triangle vertices
				vector<slVec3> m_Vertices;
				vector<slVec2> m_TexCoords;
			public:
				void apply() {
					glVertexPointer(3, GL_FLOAT, 0, &m_Vertices[0]);
					glDrawArrays(GL_TRIANGLES, 0, m_Vertices.size());
				}
		};
		
		///Represents a single material, all children will have this
		///material until another material node is encountered
		class MiniMaterialNode : public MiniNode {
			private:
				string m_Material;
				shared_ptr<MaterialManager> m_MaterialManager;
			public:
				void apply() {
					m_MaterialManager->useMaterial(m_Material);
					applyChildren();
				}
			
		};
		
		
		///A single node of the octree
		class OctreeNode {
			private:
				AABB 	m_Bounds;
				slVec3 	m_Centre;
				float 	m_Width;
				
				//8 children for this octree
				shared_ptr<OctreeNode> m_Children[8];
				
				//This is a mini tree containing the materials and geom
				//for this octreenode
				shared_ptr<MiniNode> m_RootChild;
				shared_ptr<MaterialManager> m_MaterialManager;
				
			public:
				OctreeNode();
				~OctreeNode();
				
				bool buildTree(	const vector<WorldVertex>& vertices,
								const vector<WorldTriangle>& polys, 
								const float nodeWidth, 
								const slVec3& centre);
		};
		
		class Octree {
			private:
				shared_ptr<OctreeNode> m_RootNode;
				
				AABB m_WorldBounds;
				
				void getWorldBounds(WorldMesh& mesh);
				slVec3 calcCentre(const AABB& bounds);
			public:
				Octree();
				~Octree();
				
				shared_ptr<OctreeNode> getRootNode();
				
				bool buildTree(WorldMesh& mesh);
		};
	}
}

#endif // OCTREE_H_INCLUDED
